package com.ifengxue.ioc.util;

import com.ifengxue.ioc.bean.BeanException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * 类工具
 */
public class ClassUtil {
    /** 类文件后缀名 */
    private static final String CLASS_SUFFIX = ".class";

    /**
     * 从指定的路径中加载所有可加载的类
     * @param jarPath
     * @param classLoader
     * @return
     */
    @Nonnull
    public static List<Class<?>> findClassesFromJarPath(@Nonnull URL jarPath, ClassLoader classLoader) {
        try {
            return findClassesFromJarPath(URLDecoder.decode(jarPath.getPath(), "UTF-8"), classLoader);
        } catch (UnsupportedEncodingException e) {
            //error...
            throw new IllegalStateException(e);
        }
    }

    /**
     * 从指定的路径中加载所有可加载的类
     * @param jarPath
     * @param classLoader
     * @return
     */
    @Nonnull
    public static List<Class<?>> findClassesFromJarPath(@Nonnull String jarPath, @Nullable ClassLoader classLoader) {
        classLoader = classLoader == null ? ClassUtil.class.getClassLoader() : classLoader;
        List<Class<?>> classes = new LinkedList<>();
        try {
            JarFile jarFile = new JarFile(jarPath);
            Enumeration<JarEntry> entryEnumeration = jarFile.entries();
            while (entryEnumeration.hasMoreElements()) {
                JarEntry next = entryEnumeration.nextElement();
                String name = next.getName();
                if (!name.endsWith(CLASS_SUFFIX)) {
                    continue;
                }
                try {
                    name = name.substring(0, name.length() - CLASS_SUFFIX.length());
                    classes.add(classLoader.loadClass(name.replace('/', '.')));
                } catch (ReflectiveOperationException | NoClassDefFoundError e) {
                    //do nothing
                }
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return classes;
    }

    /**
     * 从指定的路径中加载所有的类
     * @param classpath
     * @param classLoader
     * @return
     */
    @Nonnull
    public static List<Class<?>> findClassesFromClasspath(@Nonnull URL classpath, @Nullable ClassLoader classLoader) {
        try {
            String path = URLDecoder.decode(classpath.getPath(), "UTF-8");
            path = path.substring(1);//skip first char, first char is '/'
            path = path.replace('/', File.separatorChar);
            return findClassesFromClasspath(path, classLoader);
        } catch (UnsupportedEncodingException e) {
            //error...
            throw new IllegalStateException(e);
        }
    }

    /**
     * 从指定的路径中加载所有的类
     * @param classpath
     * @param classLoader
     * @return
     */
    @Nonnull
    public static List<Class<?>> findClassesFromClasspath(@Nonnull String classpath, @Nullable ClassLoader classLoader) {
        classLoader = classLoader == null ? ClassUtil.class.getClassLoader() : classLoader;
        //过滤路径末尾的/或\
        classpath = classpath.endsWith("/") || classpath.endsWith("\\") ? classpath.substring(0, classpath.length() - 1) : classpath;
        List<File> files = FileUtil.listFiles(classpath);
        List<Class<?>> classes = new LinkedList<>();
        char pathSeparator = File.separatorChar;
        for (File file : files) {
            String path = file.getPath();
            if (!path.endsWith(CLASS_SUFFIX)) {
                continue;
            }
            path = path.substring(classpath.length() + 1);
            String className = path.substring(0, path.length() - CLASS_SUFFIX.length());
            try {
                classes.add(classLoader.loadClass(className.replace(pathSeparator, '.')));
            } catch (ReflectiveOperationException | NoClassDefFoundError e) {
                //do nothing
            }
        }
        return classes;
    }

    /**
     * 获取类中声明的所有属性
     * @param clazz
     * @return
     */
    public static List<Field> findFields(Class<?> clazz) {
        List<Field> fields = new LinkedList<>();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            fields.add(field);
        }
        clazz = clazz.getSuperclass();
        if (clazz != null) {
            fields.addAll(findFields(clazz));
        }
        return fields.stream().distinct().collect(Collectors.toList());
    }



    /**
     * 创建类的新实例
     * @param clazz
     * @return
     * @throws BeanException 创建新实例失败
     */
    public static Object newInstance(Class<?> clazz) throws BeanException {
        try {
            return clazz.newInstance();
        } catch (ReflectiveOperationException e) {
            throw new BeanException("创建" + clazz.getName() + "实例失败.", e);
        }
    }
}
